package ongd.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import ongd.negocio.gestion.ControladorIndice;
import ongd.negocio.gestion.Indice;
import ongd.servicios.persistencia.exceptions.PreexistingEntityException;
import org.primefaces.context.RequestContext;
import org.primefaces.event.NodeSelectEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;

/**
 * Clase que se encargará de editar el menu insertado por los usuarios.
 *
 * @author <a href="mailto:egi0002@alu.ubu.es">Elena García Iglesias</a>
 * @author <a href="mailto:cms0021@alu.ubu.es">Cristina Martínez Sanz</a>
 * @version 1.0 20120124
 *
 */
@ManagedBean
@ViewScoped
public class EditarMenu extends ABean {

    /**
     * Objeto de tipo ControladorIndice que nos servirá para realizar las 
     * operaciones necesarias del menú.
     */
    private ControladorIndice contIndice;
    /**
     * Objeto de tipo TreeNode que nos servirá para mostrar el menu editable.
     */
    private TreeNode menu;
    /**
     * Objeto de tipo TreeNode que corresponde con la página que seleccionamos.
     */
    private TreeNode paginaSeleccionada;
    /**
     * Objeto de tipo String que corresponde con un elemento nuevo del menu.
     */
    private String nombrePagina;
    /**
     * Objeto de tipo boolean que indica si el menú ha sido editado.
     */
    private boolean menuModificado;
    /**
     * Objeto de tipo List que contiene los elementos del menu que no tienen
     * una página asociada.
     */
    private List<String> listaPaginas;

    /**
     * Constructo de la clase.
     */
    public EditarMenu() {
        SesionBean sesion = getSesionBean();
        if (sesion.iniciadaSesion()) {
            contIndice = sesion.getControladorIndice();
            nombrePagina = new String();
            contIndice.cargarHashMap();
            menu = new DefaultTreeNode("Root", null);
            menuModificado = false;
            cargarTree();
            listaPaginas = new ArrayList<String>();
        }
    }

    /**
     * Método que carga las variables iniciales tras elegir la página deseada.
     */
    @Override
    public void cargaInicial() {
    }

    /**
     * Método que carga el menu editable en función de los datos que 
     * contiene la base de datos.
     */
    private void cargarTree() {
        List<Indice> indices = contIndice.obtenerIndice();
        //Cojo el padre de todos los elementos de indice
        Indice principal = indices.get(0);
        if (principal.getNombre().compareTo("Principal") == 0) {
            TreeNode nodoPrincipal = new DefaultTreeNode(principal.getNombre(), menu);
            annadirTreeNode(principal.getIndiceList(), nodoPrincipal);
        }

    }

    /**
     * Método recursivo que añade elementos recursivamente al menu editable.
     * @param hijos Hijos del menu
     * @param padre Padre al que asociaremos los elementos añadidos
     */
    private void annadirTreeNode(List<Indice> hijos, TreeNode padre) {
        for (Indice i : hijos) {
            TreeNode nodoPadre = new DefaultTreeNode(i.getNombre(), padre);
            if (!i.getIndiceList().isEmpty()) {
                annadirTreeNode(i.getIndiceList(), nodoPadre);
            }
        }
    }

    /**
     * Método que selecciona una página.
     * @param event Evento que selecciona la página
     */
    public void seleccionarPagina(NodeSelectEvent event) {
        paginaSeleccionada = event.getTreeNode();
        getSesionBean().setStrVariable(paginaSeleccionada.toString());
    }

    /**
     * Método que añade una página al menu editable.
     * @return null
     */
    public String annadirPagina() {
        if (paginaSeleccionada != null) {
            RequestContext requestContext = RequestContext.getCurrentInstance();
            //Comprobaré los datos
            if (nombrePagina.compareTo("") == 0) {
                requestContext.addCallbackParam("vacio", true);
            } else {
                if (comprobar(menu)) {
                    requestContext.addCallbackParam("yaExiste", true);
                } else {
                    TreeNode nuevo = new DefaultTreeNode(nombrePagina, paginaSeleccionada);
                    paginaSeleccionada.setExpanded(true);
                    menuModificado = true;
                }
            }
        }
        paginaSeleccionada = new DefaultTreeNode();
        nombrePagina = new String();
        return null;
    }

    /**
     * Método recursivo que comprueba que todos los datos de la nueva página
     * son correctos.
     * @param padre TreeNode para ir recorriendole
     * @return true o false en función de si es correcto o no
     */
    public boolean comprobar(TreeNode padre) {
        boolean nombreRepetido = false;
        if (padre.getChildren().isEmpty()) {
            if (padre.toString().compareTo(nombrePagina) == 0) {
                return true;
            }
        } else {
            for (TreeNode t : padre.getChildren()) {
                if (nombreRepetido == false) {
                    nombreRepetido = comprobar(t);
                }
            }
        }
        return nombreRepetido;
    }

    /**
     * Método que quita una página del menú editable.
     * @return null
     */
    public String quitarPagina() {
        RequestContext requestContext = RequestContext.getCurrentInstance();
        if (paginaSeleccionada != null) {
            if (paginaSeleccionada.getParent() != menu) {
                if (!paginaSeleccionada.getChildren().isEmpty()) {
                    //Eliminaré los hijos.
                    for (int i = paginaSeleccionada.getChildCount() - 1; i >= 0; i--) {
                        paginaSeleccionada.getChildren().remove(i);
                    }
                }
                //Despues de eliminar los hijos, si los tiene, obtengo su padre y le busco para borrarle a el mismo.
                TreeNode padre = paginaSeleccionada.getParent();
                for (int i = padre.getChildCount() - 1; i >= 0; i--) {
                    if (padre.getChildren().get(i) == paginaSeleccionada) {
                        padre.getChildren().remove(i);
                        padre.setExpanded(true);
                    }
                }
            }
            menuModificado = true;
            paginaSeleccionada = new DefaultTreeNode();
        } else {
            requestContext.addCallbackParam("eliminarPrincipal", true);
        }
        return null;
    }

    /**
     * Método que guarda el menu en la base de datos, en caso de faltar 
     * paginas asociadas mostrará un mensaje.
     * @return null
     */
    public String guardarMenu() {
        try {
            RequestContext requestContext = RequestContext.getCurrentInstance();
            contIndice.guardarIndice(menu);
            if (!contIndice.comprobarDatos()) {
                requestContext.addCallbackParam("noHijo", true);
            } else {
                listaPaginas = contIndice.comprobarIndice();
                if (listaPaginas.isEmpty()) {
                    requestContext.addCallbackParam("correcto", true);
                } else {
                    requestContext.addCallbackParam("incorrecto", true);
                }
            }
            nombrePagina = new String();
            menuModificado = false;
        } catch (PreexistingEntityException ex) {
            Logger.getLogger(EditarMenu.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(EditarMenu.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    /**
     * Método que edita un elemento del menu.
     * @return null
     */
    public String editarElementoMenu() {
        try {
            if (menuModificado) {
                //Guardaré el menu en la base de datos
                contIndice.guardarIndice(menu);
                menuModificado = false;
            }
        } catch (PreexistingEntityException ex) {
            Logger.getLogger(EditarMenu.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(EditarMenu.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    /**
     * Método que carga el menú de la págna principal.
     * @return null
     */
    public String cargarWeb() {
        getSesionBean().cargarMenu();
        try {
            FacesContext.getCurrentInstance().getExternalContext().redirect("/AplicacionONGD/");
        } catch (IOException ex) {
            Logger.getLogger(EditarMenu.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    /**
     * Método que devuelve el valor de la variable nombrePagina.
     * @return nombrePagina
     * @see #setNombrePagina
     */
    public String getNombrePagina() {
        return nombrePagina;
    }

    /**
     * Método que establece el valor de la variable nombrePagina.
     * @param nombrePagina Nuevo valor de la variable
     * @see #getNombrePagina
     */
    public void setNombrePagina(String nombrePagina) {
        this.nombrePagina = nombrePagina;
    }

    /**
     * Método que devuelve el valor de la variable menu.
     * @return menu
     * @see #setMenu
     */
    public TreeNode getMenu() {
        return menu;
    }

    /**
     * Método que establece el valor de la variable menu.
     * @param menu Nuevo valor de la variable
     * @see #getMenu
     */
    public void setMenu(TreeNode menu) {
        this.menu = menu;
    }

    /**
     * Método que devuelve el valor de la página seleccionada.
     * @return paginaSeleccionada
     * @see #setPaginaSeleccionada
     */
    public TreeNode getPaginaSeleccionada() {
        return paginaSeleccionada;
    }

    /**
     * Método que establece el valor de la página seleccionada.
     * @param paginaSeleccionada Nuevo valor de la variable
     * @see #getPaginaSeleccionada
     */
    public void setPaginaSeleccionada(TreeNode paginaSeleccionada) {
        this.paginaSeleccionada = paginaSeleccionada;
    }

    /**
     * Método que devuelve el valor de la variable contIndice.
     * @return contIndice
     * @see #setContIndice
     */
    public ControladorIndice getContIndice() {
        return contIndice;
    }

    /**
     * Método que establece el valor de la variable contIndice.
     * @param contIndice Nuevo valor de la variable
     * @see #getContIndice
     */
    public void setContIndice(ControladorIndice contIndice) {
        this.contIndice = contIndice;
    }

    /**
     * Método que devuelve el valor de la variable menuModificado.
     * @return menuModificado
     */
    public boolean isMenuModificado() {
        return menuModificado;
    }

    /**
     * Método que establece el valor de la variable menuModificado.
     * @param menuModificado Nuevo valor de la variable
     */
    public void setMenuModificado(boolean menuModificado) {
        this.menuModificado = menuModificado;
    }

    /**
     * Método que devuelve el valor de la variable listaPaginas.
     * @return listaPaginas
     * @see #setListaPaginas
     */
    public List<String> getListaPaginas() {
        return listaPaginas;
    }

    /**
     * Método que establece el valor de la variable listaPaginas.
     * @param listaPaginas Nuevo valor de la variable
     * @see #getListaPaginas
     */
    public void setListaPaginas(List<String> listaPaginas) {
        this.listaPaginas = listaPaginas;
    }
}
